package com.zhuliwei.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhuliwei.constants.SystemConstants;
import com.zhuliwei.domain.ResponseResult;
import com.zhuliwei.domain.dto.AddArticleDto;
import com.zhuliwei.domain.dto.ArticleDto;
import com.zhuliwei.domain.entity.*;
import com.zhuliwei.domain.vo.ArticleDetailVo;
import com.zhuliwei.domain.vo.ArticleVo;
import com.zhuliwei.domain.vo.HotAndNewArticleVo;
import com.zhuliwei.domain.vo.PageVo;
import com.zhuliwei.enums.AppHttpCodeEnum;
import com.zhuliwei.exception.SystemException;
import com.zhuliwei.mapper.ArticleMapper;
import com.zhuliwei.service.ArticleService;
import com.zhuliwei.service.ArticleTagService;
import com.zhuliwei.service.CategoryService;
import com.zhuliwei.service.TagService;
import com.zhuliwei.utils.BeanCopyUtils;
import com.zhuliwei.utils.RedisCache;
import com.zhuliwei.utils.SecurityUtils;
import com.zhuliwei.utils.StringUtil;
import jakarta.annotation.Resource;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.zhuliwei.constants.RabbitConstants.NOTICE_ARTICLE_KEY;
import static com.zhuliwei.constants.RabbitConstants.NOTICE_EXCHANGE_NAME;

/**
 * 文章接口实现类
 *
 * @author 朱力维
 * @time 2022/10/7 10:48
 **/
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
    @Lazy
    @Resource
    private CategoryService categoryService;

    @Resource
    private RedisCache redisCache;

    @Resource
    private ArticleTagService articleTagService;

    @Resource
    private TagService tagService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public ResponseResult hotArticleList() {
        //查询热门文章 封装成ResponseResult返回
        LambdaQueryWrapper<Article> condition = new LambdaQueryWrapper<>();
        //必须是正式文章（非删除，非草稿）
        //实际项目中不允许直接在代码中使用字面值。都需要定义成常量来使用。这种方式有利于提高代码的可维护性。
        condition.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
        //按照浏览量进行降序排序
        condition.orderByDesc(Article::getViewCount);
        //分页：最多只查询5条文章
        Page<Article> page = new Page<>(1, 5);
        page(page, condition);
        //获取查到的数据
        List<Article> articles = page.getRecords();
        Map<String, Integer> viewCountMap = redisCache.getCacheMap(SystemConstants.VIEWCOUNT_MAP_PREFIX);
        articles.forEach(article -> {
            if (null != viewCountMap.get(article.getId())) {
                Long viewCount = viewCountMap.get(article.getId()).longValue();
                article.setViewCount(viewCount);
            }
        });
        //使用vo给前端传去需要的数据
        List<HotAndNewArticleVo> articleVos = BeanCopyUtils.copyBeanList(articles, HotAndNewArticleVo.class);
        return ResponseResult.okResult(articleVos);
    }

    @Override
    public ResponseResult articleList(Integer pageNum, Integer pageSize, String categoryId, String paramsId) {
        // 如果有标签Id
        if (StringUtils.hasText(paramsId)) {
            return listByTag(paramsId);
        } else {
//查出所有非置顶的
            LambdaQueryWrapper<Article> condition = new LambdaQueryWrapper();
            //如果有categoryId就要查询时要和传入的相同
            condition.eq(Objects.nonNull(categoryId), Article::getCategoryId, categoryId);
            //状态是正式发布的
            condition.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
            //不是置顶的
            condition.orderByDesc(Article::getIsTop);
            //对文章发布时间进行降序
            condition.orderByDesc(Article::getCreateTime);
            //分页查询
            Page<Article> page = null;
            List<Article> topList = new ArrayList<>();
            //如果是第一页就需要查出所有置顶的
            if (pageNum == 1) {
                LambdaQueryWrapper<Article> lqw = new LambdaQueryWrapper<>();
                //如果有categoryId就要查询时要和传入的相同
                lqw.eq(Objects.nonNull(categoryId), Article::getCategoryId, categoryId);
                //状态是正式发布的
                lqw.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
                //是置顶的
                lqw.eq(Article::getIsTop, SystemConstants.ARTICLE_IS_TOP);
                //对文章发布时间进行降序
                lqw.orderByDesc(Article::getCreateTime);
                topList = list(lqw);
            }
            page = new Page<>(pageNum, pageSize);
            List<Article> articles = new ArrayList<>();
            page(page, condition);
            if (topList.size() != 0) {
                articles = topList.stream().collect(Collectors.toList());
                for (int i = topList.size(); i < page.getRecords().size(); i++) {
                    articles.add(page.getRecords().get(i));
                }
            } else {
                articles = page.getRecords();
            }
            //查询分类名
            articles.forEach(article -> {
                if (Objects.nonNull(article.getCategoryId())) {
                    Category category = categoryService.getById(article.getCategoryId());
                    article.setCategoryName(category.getName());
                } else {
                    article.setCategoryName("无");
                }
            });
            //封装查询结果
            List<ArticleVo> articleVos = BeanCopyUtils.copyBeanList(articles, ArticleVo.class);
            articleVos.forEach(articleVo -> {
                // 从redis获取浏览量
                if (null != redisCache.getCacheMapValue(SystemConstants.VIEWCOUNT_MAP_PREFIX, articleVo.getId())) {
                    Integer viewCount = redisCache.getCacheMapValue(SystemConstants.VIEWCOUNT_MAP_PREFIX, articleVo.getId());
                    articleVo.setViewCount(viewCount.longValue());
                }
                // 从redis获取点赞集合
                if (null != redisCache.getCacheMapValue(SystemConstants.LIKES_MAP_PREFIX, articleVo.getId())) {
                    HashSet likes = redisCache.getCacheMapValue(SystemConstants.LIKES_MAP_PREFIX, articleVo.getId());
                    articleVo.setLikeCount(Long.parseLong(String.valueOf(likes.size())));
                }
                // 获取标签名
                LambdaQueryWrapper<ArticleTag> atlqw = new LambdaQueryWrapper<>();
                atlqw.eq(ArticleTag::getArticleId, articleVo.getId());
                List<ArticleTag> articleTags = articleTagService.list(atlqw);
                List<String> tagsName = new ArrayList<>();
                articleTags.forEach(articleTag -> {
                    Tag tag = tagService.getById(articleTag.getTagId());
                    tagsName.add(tag.getName());
                });
                articleVo.setTags(tagsName);
            });
            PageVo pageVo = new PageVo(articleVos, page.getTotal());
            return ResponseResult.okResult(pageVo);
        }
    }

    @Override
    public ResponseResult getArticleDetail(String id) {
        // 根据Id查询文章
        Article article = getById(id);
        // 从redis获取浏览量
        if (null != redisCache.getCacheMapValue(SystemConstants.VIEWCOUNT_MAP_PREFIX, id)) {
            Integer viewCount = redisCache.getCacheMapValue(SystemConstants.VIEWCOUNT_MAP_PREFIX, id);
            article.setViewCount(viewCount.longValue());
        }
        // 从redis获取点赞集合
        if (null != redisCache.getCacheMapValue(SystemConstants.LIKES_MAP_PREFIX, id)) {
            String likes = redisCache.getCacheMapValue(SystemConstants.LIKES_MAP_PREFIX, id).toString();
            article.setLikes(likes);
        }
        Set<String> likeSet = StringUtil.stringToSet(article.getLikes());
        String userId = "";
        // 获取登录用户id
        try {
            userId = SecurityUtils.getUserId();
        } catch (Exception e) {
            System.out.println(e);
        }
        if (StringUtils.hasText(userId)) {
            // 是否已经点赞
            if (likeSet.contains(userId))
                article.setLiked(true);
            else
                article.setLiked(false);
        } else {
            //未登录用户默认为未点赞
            article.setLiked(false);
        }
        // 转换成VO
        ArticleDetailVo articleDetailVo = BeanCopyUtils.copyBean(article, ArticleDetailVo.class);
        // 获取标签名
        LambdaQueryWrapper<ArticleTag> atlqw = new LambdaQueryWrapper<>();
        atlqw.eq(ArticleTag::getArticleId, id);
        List<ArticleTag> articleTags = articleTagService.list(atlqw);
        List<String> tagsName = new ArrayList<>();
        articleTags.forEach(articleTag -> {
            Tag tag = tagService.getById(articleTag.getTagId());
            tagsName.add(tag.getName());
        });
        articleDetailVo.setTags(tagsName);
        // 根据分类id查询分类名
        if (Objects.nonNull(article.getCategoryId())) {
            articleDetailVo.setCategoryName(categoryService.getById(article.getCategoryId()).getName());
        } else {
            articleDetailVo.setCategoryName("无");
        }
        // 点赞数
        articleDetailVo.setLikeCount(Long.parseLong(String.valueOf(likeSet.size())));
        // 封装响应返回
        return ResponseResult.okResult(articleDetailVo);
    }

    @Override
    public ResponseResult updateViewCount(String id) {
        //更新redis中对应id的浏览量
        redisCache.incrementCacheMapValue(SystemConstants.VIEWCOUNT_MAP_PREFIX, id, 1);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult getArticleData() {
        ArticleData articleData = new ArticleData();
        LambdaQueryWrapper<Article> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
        Long count = count(lqw);
        // 总文章数
        articleData.setTotalArticleNum(count.intValue());
        // 总标签数
        articleData.setTotalTagNum(Long.parseLong(String.valueOf(tagService.count())));
        lqw.select(Article::getId);
        Map<String, Integer> viewCountMap = redisCache.getCacheMap(SystemConstants.VIEWCOUNT_MAP_PREFIX);
        List<Article> articleList = list(lqw);
        articleList.forEach(article -> {
            if (null != viewCountMap.get(article.getId())) {
                // 总浏览量
                Long viewCount = viewCountMap.get(article.getId()).longValue();
                article.setViewCount(viewCount);
            } else {
                article.setViewCount(0L);
            }
        });

        Long totalViewCount = 0L;
        for (Article article : articleList
        ) {
            totalViewCount += article.getViewCount();
        }
        articleData.setTotalViewCount(totalViewCount);
        return ResponseResult.okResult(articleData);
    }

    @Override
    public ResponseResult<PageVo> pageArticleList(Integer pageNum, Integer pageSize, ArticleDto articleDto) {
        //分页查询
        LambdaQueryWrapper<Article> lqw = new LambdaQueryWrapper();
        lqw.like(StringUtils.hasText(articleDto.getTitle()), Article::getTitle, articleDto.getTitle());
        lqw.like(StringUtils.hasText(articleDto.getSummary()), Article::getSummary, articleDto.getSummary());
        lqw.like(StringUtils.hasText(articleDto.getIsTop()), Article::getIsTop, articleDto.getIsTop());
        lqw.orderByDesc(Article::getCreateTime);
        Page<Article> page = new Page<>();
        page.setCurrent(pageNum);
        page.setSize(pageSize);
        page(page, lqw);
        //封装数据返回
        List<Article> articles = page.getRecords();
        List<ArticleVo> articleVos = BeanCopyUtils.copyBeanList(articles, ArticleVo.class);
        PageVo pageVo = new PageVo(articleVos, page.getTotal());
        return ResponseResult.okResult(pageVo);
    }

    @Override
    @Transactional //事务注解
    public ResponseResult add(AddArticleDto articleDto) {
        if (!StringUtils.hasText(articleDto.getCategoryId())) {
            throw new SystemException(AppHttpCodeEnum.CATEGORY_NULL);
        }
        if (CollectionUtils.isEmpty(articleDto.getTags())) {
            throw new SystemException(AppHttpCodeEnum.TAG_NAME_NULL);
        }
        if (articleDto.getIsTop().equals(SystemConstants.ARTICLE_IS_TOP)) {
            LambdaQueryWrapper<Article> lqw = new LambdaQueryWrapper();
            lqw.eq(Article::getIsTop, SystemConstants.ARTICLE_IS_TOP);
            if (count(lqw) >= 5) {
                throw new SystemException(AppHttpCodeEnum.ARTICLE_TOP_MAX);
            }
        }
        //添加 博客
        Article article = BeanCopyUtils.copyBean(articleDto, Article.class);
        save(article);

        //一个文章最多只能添加三个标签
        if (!CollectionUtils.isEmpty(articleDto.getTags()) || 0 != articleDto.getTags().size()) {
            if (articleDto.getTags().size() > 3) {
                throw new SystemException(AppHttpCodeEnum.ARTICLE_TAG_MAX);
            }
        }

        List<ArticleTag> articleTags = articleDto.getTags().stream()
                .map(tagId -> new ArticleTag(article.getId(), tagId))
                .collect(Collectors.toList());

        //添加 博客和标签的关联
        articleTagService.saveBatch(articleTags);
        // 文章添加成功且是正式发布的不是草稿通知所有前台用户（异步）
        if (articleDto.getStatus().equals("0"))
            rabbitTemplate.convertAndSend(NOTICE_EXCHANGE_NAME, NOTICE_ARTICLE_KEY, article);
        return ResponseResult.okResult();
    }

    @Override
    @Transactional
    public ResponseResult updateArticle(AddArticleDto articleDto) {
        Article article1 = getById(articleDto.getId());
        // 原本不是置顶的文章才做判断
        if (!article1.getIsTop().equals(SystemConstants.ARTICLE_IS_TOP)) {
            if (articleDto.getIsTop().equals(SystemConstants.ARTICLE_IS_TOP)) {
                LambdaQueryWrapper<Article> lqw = new LambdaQueryWrapper();
                lqw.eq(Article::getIsTop, SystemConstants.ARTICLE_IS_TOP);
                if (count(lqw) >= 5) {
                    throw new SystemException(AppHttpCodeEnum.ARTICLE_TOP_MAX);
                }
            }
        }
        Article article = BeanCopyUtils.copyBean(articleDto, Article.class);
        article.setUpdateTime(new Date());
        article.setUpdateBy(SecurityUtils.getUserId());
        updateById(article);

        // 删除原来的标签
        LambdaQueryWrapper<ArticleTag> atlqw = new LambdaQueryWrapper<>();
        atlqw.eq(ArticleTag::getArticleId, articleDto.getId());
        articleTagService.remove(atlqw);
        // 修改标签
        List<String> tags = articleDto.getTags();


        // 一个文章最多添加三个标签
        if (!CollectionUtils.isEmpty(tags) || 0 != tags.size()) {
            if (tags.size() > 3) {
                throw new SystemException(AppHttpCodeEnum.ARTICLE_TAG_MAX);
            }
        }

        List<ArticleTag> articleTags = tags.stream()
                .map(tagId ->
                        new ArticleTag(article.getId(), tagId))
                .collect(Collectors.toList());
        //添加 博客和标签的关联
        articleTagService.saveBatch(articleTags);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult newArticleList() {
        //查询最新文章 封装成ResponseResult返回
        LambdaQueryWrapper<Article> condition = new LambdaQueryWrapper<>();
        //必须是正式文章（非删除，非草稿）
        //实际项目中不允许直接在代码中使用字面值。都需要定义成常量来使用。这种方式有利于提高代码的可维护性。
        condition.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
        //按照发布时间进行降序排序
        condition.orderByDesc(Article::getCreateTime);
        //分页：最多只查询5条文章
        Page<Article> page = new Page<>(1, 5);
        page(page, condition);
        //获取查到的数据
        List<Article> articles = page.getRecords();
        //使用vo给前端传去需要的数据
        List<HotAndNewArticleVo> articleVos = BeanCopyUtils.copyBeanList(articles, HotAndNewArticleVo.class);
        return ResponseResult.okResult(articleVos);
    }

    @Override
    public ResponseResult search(String keyword) {
        LambdaQueryWrapper<Article> titleLqw = new LambdaQueryWrapper<>();
        titleLqw.select(Article::getTitle, Article::getSummary, Article::getId, Article::getThumbnail);
        titleLqw.like(Article::getTitle, keyword);
        List<Article> list = list(titleLqw);
        LambdaQueryWrapper<Article> summaryLqw = new LambdaQueryWrapper<>();
        summaryLqw.select(Article::getTitle, Article::getSummary, Article::getId, Article::getThumbnail);
        summaryLqw.like(Article::getSummary, keyword);
        List<Article> listBySummary = list(summaryLqw);
        // 去重：去除重复文章
        if (!CollectionUtils.isEmpty(listBySummary)) {
            listBySummary.forEach(summary -> {
                Iterator<Article> iterator = list.iterator();
                while (iterator.hasNext()) {
                    Article a = iterator.next();
                    if (a.getId().equals(summary.getId())) {
                        iterator.remove();
                    }
                }
                list.add(summary);
            });
        }
        return ResponseResult.okResult(list);
    }

    @Override
    public ResponseResult getMyLikes() {
        String myId = SecurityUtils.getUserId();
        Set<Article> myLikes = new HashSet<>();
        // 从redis中获取赞过的文章
        Map<String, Object> likeMap = redisCache.getCacheMap(SystemConstants.LIKES_MAP_PREFIX);
        if (!CollectionUtils.isEmpty(likeMap)) {
            likeMap.forEach((key, value) -> {
                if (Objects.nonNull(value) && StringUtil.stringToSet(value.toString()).contains(myId)) {
                    Article article = getById(key);
                    Category category = categoryService.getById(article.getCategoryId());
                    article.setCategoryName(category.getName());
                    myLikes.add(article);
                }
            });
        }
        return ResponseResult.okResult(myLikes);
    }

    /**
     * 根据标签Id查询所有文章
     */
    public ResponseResult<PageVo> listByTag(String paramsId) {
        LambdaQueryWrapper<ArticleTag> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ArticleTag::getTagId, paramsId);
        List<ArticleTag> articleTagList = articleTagService.list(lqw);
        List<String> articleIds = articleTagList.stream().map(articleTag -> articleTag.getArticleId()).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(articleIds)) {
            List<Article> articleList = listByIds(articleIds);
            List<ArticleVo> articleVoList = articleIds.stream().map(s -> {
                LambdaQueryWrapper<Article> alqw = new LambdaQueryWrapper<>();
                //状态是正式发布的
                alqw.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
                alqw.eq(Article::getId, s);
                Article article = getOne(alqw);
                // 从redis获取浏览量
                if (null != redisCache.getCacheMapValue(SystemConstants.VIEWCOUNT_MAP_PREFIX, article.getId())) {
                    Integer viewCount = redisCache.getCacheMapValue(SystemConstants.VIEWCOUNT_MAP_PREFIX, article.getId());
                    article.setViewCount(viewCount.longValue());
                }
                // 获取标签名
                LambdaQueryWrapper<ArticleTag> atlqw = new LambdaQueryWrapper<>();
                atlqw.eq(ArticleTag::getArticleId, article.getId());
                List<ArticleTag> articleTags = articleTagService.list(atlqw);
                List<String> tagsName = new ArrayList<>();
                articleTags.forEach(articleTag -> {
                    Tag tag = tagService.getById(articleTag.getTagId());
                    tagsName.add(tag.getName());
                });
                ArticleVo articleVo = BeanCopyUtils.copyBean(article, ArticleVo.class);
                articleVo.setTags(tagsName);
                // 查询分类名
                if (Objects.nonNull(article.getCategoryId())) {
                    Category category = categoryService.getById(article.getCategoryId());
                    articleVo.setCategoryName(category.getName());
                } else {
                    articleVo.setCategoryName("无");
                }
                // 标签查询就不需要把置顶显示最上面了
                articleVo.setIsTop(SystemConstants.ARTICLE_NOT_TOP);
                return articleVo;
            }).collect(Collectors.toList());
            //集合内容根据时间降序排序
            Comparator<Object> objectComparator = Collections.reverseOrder();
            articleVoList.sort(Comparator.comparing(ArticleVo::getCreateTime, objectComparator));
            Integer total = articleVoList.size();
            return ResponseResult.okResult(new PageVo(articleVoList, total.longValue()));
        }
        return ResponseResult.okResult(new PageVo(new ArrayList<>(), 0L));
    }
}

